package com.cardone.context;

import java.util.HashMap;
import java.util.Map;

import lombok.Setter;
import lombok.extern.slf4j.Slf4j;

import org.apache.commons.lang.ArrayUtils;
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;

/**
 * 上下文支持
 *
 * @author yaohaitao
 *
 */
@Slf4j
public class ContextHolder implements ApplicationContextAware {
	/**
	 * Spring ApplicationContext
	 */
	private static ApplicationContext applicationContext;

	/**
	 * beanMap
	 */
	@Setter
	private static Map<String, String> beanMap;

	/**
	 * 标识
	 */
	private static ThreadLocal<String> idThreadLocal = new ThreadLocal<String>();

	/**
	 * 对象映射
	 */
	private static ThreadLocal<Map<String, Object>> objectMapThreadLocal = new ThreadLocal<Map<String, Object>>();

	/**
	 * 字符串映射
	 */
	private static ThreadLocal<Map<String, String>> stringMapThreadLocal = new ThreadLocal<Map<String, String>>();

	/**
	 * 清除:标识
	 */
	public static void clearId() {
		ContextHolder.idThreadLocal.remove();
	}

	/**
	 * 清除字符串值
	 */
	public static void clearStringValue() {
		ContextHolder.stringMapThreadLocal.remove();
	}

	/**
	 * 获取：应用上下文
	 *
	 * @return 应用上下文
	 */
	public static ApplicationContext getApplicationContext() {
		return ContextHolder.applicationContext;
	}

	/**
	 * 获取 bean
	 *
	 * @param requiredType
	 *          类型
	 * @return bean
	 */
	public static <T> T getBean(final Class<T> requiredType) {
		return ContextHolder.getBean(requiredType, requiredType.getName());
	}

	/**
	 * 获取 bean
	 *
	 * @param name
	 *          名称
	 *
	 * @param requiredType
	 *          类型
	 * @return bean
	 */
	public static <T> T getBean(final Class<T> requiredType, final String name) {
		if (ContextHolder.applicationContext == null) {
			return null;
		}

		if (ContextHolder.applicationContext.containsBean(name)) {
			return ContextHolder.applicationContext.getBean(name, requiredType);
		}

		final String[] beanNames = ContextHolder.applicationContext.getBeanNamesForType(requiredType);

		if (ArrayUtils.isEmpty(beanNames)) {
			ContextHolder.log.error("未配置spring bean:" + requiredType.getName());

			return null;
		}

		if (ArrayUtils.getLength(beanNames) > 1) {
			throw new DictionaryException("配置的spring bean过多:" + beanNames);
		}

		return ContextHolder.applicationContext.getBean(beanNames[0], requiredType);
	}

	/**
	 * 获取：标识
	 *
	 * @return 标识
	 */
	public static String getId() {
		return ContextHolder.idThreadLocal.get();
	}

	/**
	 * 获取：对象值
	 *
	 * @param key
	 *          键
	 * @return 对象值
	 */
	public static Object getObjectValue(final String key) {
		if (ContextHolder.objectMapThreadLocal.get() == null) {
			return null;
		}

		return ContextHolder.objectMapThreadLocal.get().get(key);
	}

	/**
	 * 获取：字符串值
	 *
	 * @param key
	 *          键
	 * @return 字符串值
	 */
	public static String getStringValue(final String key) {
		if (ContextHolder.stringMapThreadLocal.get() == null) {
			return null;
		}

		return ContextHolder.stringMapThreadLocal.get().get(key);
	}

	/**
	 * 设置：标识
	 *
	 * @param value
	 *          标识
	 */
	public static void setId(final String value) {
		ContextHolder.idThreadLocal.set(value);

	}

	/**
	 * 设置：对象值
	 *
	 * @param key
	 *          键
	 * @param value
	 *          对象值
	 */
	public static void setObjectValue(final String key, final Object value) {
		if (ContextHolder.objectMapThreadLocal.get() == null) {
			ContextHolder.objectMapThreadLocal.set(new HashMap<String, Object>());
		}

		ContextHolder.objectMapThreadLocal.get().put(key, value);
	}

	/**
	 * 设置：应用上下文
	 *
	 * @param applicationContext
	 *          ApplicationContext
	 */
	public static void setSpringApplicationContext(final ApplicationContext applicationContext) {
		ContextHolder.applicationContext = applicationContext;
	}

	/**
	 * 设置：字符串值
	 *
	 * @param key
	 *          键
	 * @param value
	 *          字符串值
	 */
	public static void setStringValue(final String key, final String value) {
		if (ContextHolder.stringMapThreadLocal.get() == null) {
			ContextHolder.stringMapThreadLocal.set(new HashMap<String, String>());
		}

		ContextHolder.stringMapThreadLocal.get().put(key, value);
	}

	@Override
	public void setApplicationContext(final ApplicationContext applicationContext) throws BeansException {
		ContextHolder.setSpringApplicationContext(applicationContext);
	}
}
